/*
 * ============================================================================
 *
 *       Filename:  dynctl.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  02/06/2018 04:37:11 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jianxi sun (jianxi), ycsunjane@gmail.com
 *   Organization:  
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <assert.h>
#include <arpa/inet.h>
#include <malloc.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#include "common.h"
#include "dynctl.h"
#include "user.h"

#include "equipment.h"
static void dynctl_status(EV_P_ buffer2_t *buf)
{
	printf("Entering status...\n");
	breset(buf);
}

static void dynctl_showurl(EV_P_ buffer2_t *buf)
{
	printf("Entering showurl...\n");
	url_show(buf);
}

static void dynctl_bind(EV_P_ buffer2_t *buf, char *arg)
{
	printf("Entering bind...\n");
	int isend = 0;
	int ret = -1;

	char *username, *password;
	username = arg;
	TO_NEXT_WORD(arg, isend);
	if(!isend)
		password = arg;
	else
		goto err;

	printf("%s, %s\n", username, password);
	ret = equipment_bind(username, password);

err:
	breset(buf);
	if(ret == 0)
		bconcat(buf, "Yes", 3);
	else
		bconcat(buf, "No", 2);
}

static void dynctl_unbind(EV_P_ buffer2_t *buf, char *arg)
{
	printf("Entering unbind...\n");
	int isend = 0;
	int ret = -1;

	char *username, *password;
	username = arg;
	TO_NEXT_WORD(arg, isend);
	if(!isend)
		password = arg;
	else
		goto err;

	printf("%s, %s\n", username, password);
	ret = equipment_unbind(username, password);

err:
	breset(buf);
	if(ret == 0)
		bconcat(buf, "Yes", 3);
	else
		bconcat(buf, "No", 2);
}

extern int reconnect_add(char *nic);
extern int reconnect_del(char *nic);
static void dynctl_newcli(EV_P_ buffer2_t *buf, char *arg)
{
	printf("Entering newcli...\n");

	char *nic;
	nic = arg;
	int ret = -1;
	printf("%s\n", nic);
	ret = reconnect_add(nic); 

	breset(buf);
	if(ret == 0)
		bconcat(buf, "Yes", 3);
	else
		bconcat(buf, "No", 2);
}

static void dynctl_delcli(EV_P_ buffer2_t *buf, char *arg)
{
	printf("Entering delcli...\n");

	char *nic;
	nic = arg;
	int ret = -1;
	printf("%s\n", nic);
	ret = reconnect_del(nic); 

	breset(buf);
	if(ret == 0)
		bconcat(buf, "Yes", 3);
	else
		bconcat(buf, "No", 2);
}

void shownic(EV_P_ buffer2_t *buf);
static void dynctl_showcli(EV_P_ buffer2_t *buf)
{
	printf("Entering show client...\n");
	shownic(EV_A_ buf);
}

void free_dynctl(dynctl_t *dynctl)
{
	printf("close and free dynctl remote\n");
	if (dynctl->buf != NULL) {
		bfree(dynctl->buf);
		ss_free(dynctl->buf);
	}
	ss_free(dynctl->recv_ctx);
	ss_free(dynctl->send_ctx);
	ss_free(dynctl);
}

void close_and_free_dynctl(EV_P_ dynctl_t *dynctl)
{
	if (dynctl != NULL) {
		printf("close and free dynctl fd: %d\n", dynctl->fd);
		ev_io_stop(EV_A_ & dynctl->send_ctx->io);
		ev_io_stop(EV_A_ & dynctl->recv_ctx->io);
		close(dynctl->fd);
		free_dynctl(dynctl);
	}
}

static void dynctl_send_cb(EV_P_ ev_io *w, int revents)
{
	dynctl_ctx_t *dynctl_send_ctx = container_of(w, dynctl_ctx_t, io);
	dynctl_t *dynctl = dynctl_send_ctx->dynctl;

	if (dynctl->buf->len == 0) {
		// close and free
		close_and_free_dynctl(EV_A_ dynctl);
		return;
	} else {
		// has data to send
		ssize_t s = send(dynctl->fd, dynctl->buf->array + dynctl->buf->idx,
			dynctl->buf->len, 0);
		if (s == -1) {
			if (errno != EAGAIN && errno != EWOULDBLOCK) {
				printf("dynctl_send_cb_send");
				close_and_free_dynctl(EV_A_ dynctl);
			}
			return;
		} else if (s < (ssize_t)(dynctl->buf->len)) {
			// partly sent, move memory, wait for the next time to send
			dynctl->buf->len -= s;
			dynctl->buf->idx += s;
			return;
		} else {
			// all sent out, wait for reading
			breset(dynctl->buf);
			ev_io_stop(EV_A_ &dynctl_send_ctx->io);
		}
	}

	close_and_free_dynctl(EV_A_ dynctl);
}

static void dynctl_addurl(EV_P_ buffer2_t *buf, char *arg)
{
	printf("Entering addurl...\n");
	int ret = -1;

	char *url;
	url = arg;

	printf("%s\n", url);
	ret = url_add(url);

	breset(buf);
	if(ret == 0)
		bconcat(buf, "Yes", 3);
	else
		bconcat(buf, "No", 2);
}

static void dynctl_delurl(EV_P_ buffer2_t *buf, char *arg)
{
	printf("Entering delurl...\n");
	int ret = -1;

	char *id;
	id = arg;

	printf("%s\n", id);
	ret = url_del(id);

	breset(buf);
	if(ret == 0)
		bconcat(buf, "Yes", 3);
	else
		bconcat(buf, "No", 2);
}

static void dynctl_recv_cb(EV_P_ ev_io *w, int revents)
{
	dynctl_ctx_t *dynctl_recv_ctx = container_of(w, dynctl_ctx_t, io);
	dynctl_t *dynctl = dynctl_recv_ctx->dynctl;
	dynctl_ctx_t *dynctl_send_ctx = dynctl->send_ctx;

	ssize_t r;
	buffer2_t *buf;
	char buffer[512];
	buf = dynctl->buf;

	r = recv(dynctl->fd, buffer, sizeof(buffer), 0);
	printf("fd: %u, size: %lu\n", dynctl->fd, r);
	if (r == 0) {
		/* connection closed */
		close_and_free_dynctl(EV_A_ dynctl);
		return;
	} else if (r == -1) {
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			/* continue to wait for recv */
			return;
		} else {
			printf("recv failed: %s(%d)\n",
				strerror(errno), errno);
			close_and_free_dynctl(EV_A_ dynctl);
			return;
		}
	} else {
		bconcat(buf, buffer, r);
	}

	if(buf->array[buf->len - 2] != '\r' && 
		buf->array[buf->len - 1] != '\n') {
		printf("need more data\n");
		return;
	}

	char *tmp = strstr(buf->array, "\r\n");
	if(!tmp) {
		close_and_free_dynctl(EV_A_ dynctl);
		return;
	}
	*tmp = 0;
	printf("dynctl received: %s\n", buf->array);

	if (strncmp(buf->array, "bind", 4) == 0) {
		dynctl_bind(EV_A_ buf, buf->array + 5);
	} else if (strncmp(buf->array, "addurl", 6) == 0) {
		dynctl_addurl(EV_A_ buf, buf->array + 7);
	} else if (strncmp(buf->array, "delurl", 6) == 0) {
		dynctl_delurl(EV_A_ buf, buf->array + 7);
	} else if (strncmp(buf->array, "showurl", 7) == 0) {
		dynctl_showurl(EV_A_ buf);
	} else if (strncmp(buf->array, "unbind", 6) == 0) {
		dynctl_unbind(EV_A_ buf, buf->array + 7);
	} else if (strncmp(buf->array, "newcli", 6) == 0) {
		dynctl_newcli(EV_A_ buf, buf->array + 7);
	} else if (strncmp(buf->array, "delcli", 6) == 0) {
		dynctl_delcli(EV_A_ buf, buf->array + 7);
	} else if (strncmp(buf->array, "showcli", 7) == 0) {
		dynctl_showcli(EV_A_ buf);
	} else if (strncmp(buf->array, "status", 6) == 0) {
		dynctl_status(EV_A_ buf);
	}

	ev_io_stop(EV_A_ &dynctl_recv_ctx->io);
	ev_io_start(EV_A_ &dynctl_send_ctx->io);
	return;
}

static dynctl_t *new_dynctl(EV_P_ int fd)
{
	dynctl_t *dynctl;
	dynctl = ss_malloc(sizeof(dynctl_t));

	dynctl->loop	 = EV_A;
	dynctl->recv_ctx = ss_malloc(sizeof(dynctl_ctx_t));
	dynctl->send_ctx = ss_malloc(sizeof(dynctl_ctx_t));
	dynctl->buf      = ss_malloc(sizeof(buffer2_t));
	dynctl->fd       = fd;
	dynctl->recv_ctx->dynctl = dynctl;
	dynctl->send_ctx->dynctl = dynctl;

	balloc(dynctl->buf, BUF_SIZE);
	ev_io_init(&dynctl->recv_ctx->io, dynctl_recv_cb, fd, EV_READ);
	ev_io_init(&dynctl->send_ctx->io, dynctl_send_cb, fd, EV_WRITE);

	return dynctl;
}

int setnonblocking(int fd)
{
	int flags;
	if (-1 == (flags = fcntl(fd, F_GETFL, 0))) {
		flags = 0;
	}
	return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

void accept_dynctl_cb(EV_P_ ev_io *w, int revents)
{
	listen_ctx_t *listener = container_of(w, struct listen_ctx, io);

	int dynsockfd;
	struct sockaddr_storage storage;
	memset(&storage, 0, sizeof(storage));
	socklen_t len;
	len = (socklen_t) sizeof(storage);

	dynsockfd = accept(listener->fd, (struct sockaddr *)&storage, &len);
	if (dynsockfd == -1) {
		printf("accept failed: %s(%d)\n",
			strerror(errno), errno);
		return;
	} else {
		printf( "Accepted connection on socket %d\n",
			listener->fd);
	}
	setnonblocking(dynsockfd);

	dynctl_t *dynctl = new_dynctl(listener->loop, dynsockfd);
	dynctl->listener = listener;

	/* start recv dynctl */
	ev_io_start(listener->loop, &dynctl->recv_ctx->io);
}
